package com.example.demo.util;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.reader.SheetReader;
import cn.hutool.poi.excel.sax.Excel03SaxReader;
import cn.hutool.poi.excel.sax.handler.BeanRowHandler;
import com.example.demo.pojo.annotation.excel.ExcelColumn;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Sheet;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.awt.*;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * xml解析工具类
 *
 * @author chenzhigang
 */
public class XmlParseUtil {

    /**
     * xml字符串转对象
     *
     * @param xmlStr xml字符串
     * @param t 对象类类
     * @param <T> 对象泛型
     * @return 对象实例
     * @throws JAXBException 异常
     */
    public static <T> T xmlToObject(String xmlStr, Class<T> t) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(t);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        //noinspection unchecked
        return (T) unmarshaller.unmarshal(new StringReader(xmlStr));
    }

    /**
     * 对象转xml字符串
     *
     * @param bean 实体对象
     * @param encoding 编码
     * @param  autoWrap 是否自动换行
     * @param ignoreHeader 是否省略xml头信息
     * @return
     * @throws JAXBException
     */
    public static String  beanToXml(Object bean, String encoding, boolean autoWrap, boolean ignoreHeader) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(bean.getClass());
        Marshaller marshaller = context.createMarshaller();
        // 是否在转换成xml时同时进行格式化（即按标签自动换行，否则即是一行的xml）
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, autoWrap);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
        // 是否省略xml头信息，默认不省略（false）
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, ignoreHeader);
        StringWriter writer = new StringWriter();
        marshaller.marshal(bean, writer);
        return writer.toString();
    }

    /**
     * 对象转xml字符串
     *
     * @param bean 实体对象
     * @return
     * @throws JAXBException
     */
    public static String  beanToXml(Object bean) throws JAXBException {
        return beanToXml(bean, "UTF-8", false, true);
    }

    public static void main(String[] args) throws JAXBException {
        // ------------------------ 1.读excel数据转为bean集合 ---------------------------
        // 解析excel为bean
        String excelPath = "C:/Users/Administrator/Desktop/票据调查/清单模板-完整.xls";
        // todo 1.要不要考虑数据太多，分批处理，防止oom？？
        // todo 2.通用信息单独一个sheet页么，还是我自己去分组转成多个xml？？
        ExcelReader reader = ExcelUtil.getReader(excelPath);
        // 第一个sheet页
        reader.setSheet(0);
        // 列名映射关系
        reader.setHeaderAlias(CustomExcelUtil.getHeaderAlias(InventoryImportExcelDto.class, true));
        // 读取数据转为bean
        List<InventoryImportExcelDto> importDatas = reader.readAll(InventoryImportExcelDto.class);


        // ------------------------- 2.数据转换 -------------------
        if (CollUtil.isEmpty(importDatas)) {
            throw new RuntimeException("excel数据为空");
        }

        // 商品信息列表
        List<InvoiceItem> invoiceItems = new ArrayList<>();
        // 默认小数两位吧
//        BigDecimal decimalNum = new BigDecimal("100.005").setScale(2, BigDecimal.ROUND_DOWN);
        InventoryImportExcelDto firstDto = importDatas.get(0);

        for (InventoryImportExcelDto importData : importDatas) {
            InvoiceItem invoiceItem = InvoiceItem.builder()
                    .amount(importData.getAmount())
                    // 发票行性质。0-正常行 1-折扣行 2-被折扣行
                    .bankType(0) //?
                    .commodityCode(importData.getCommodityCode())
                    .deduction(importData.getDeduction())
                    // 零税率标识。空-非零税率 1-免税 2-不征收 3-普通零税率
                    .noTaxType(1) //?
                    .num(importData.getNum())
                    .name(importData.getName())
                    .selfCode("自行编码") //?
                    .specification(importData.getSpecification())
                    .tax(importData.getTax())
                    .taxRate(importData.getTaxRate())
                    .unit(importData.getUnit())
                    .unitPrice(importData.getUnitPrice())
                    // 优惠政策标识。0-不使用 1-使用
                    .usePreferential(importData.getUsePreferential())
                    .vat("增值税特殊管理") //?
                    .build();
            invoiceItems.add(invoiceItem);
        }


        // 商品详细信息
        InvoiceDetail invoiceDetail = new InvoiceDetail();
        invoiceDetail.setClassAttr("COMMON_FPKJ_XMXX");
        invoiceDetail.setSize(invoiceItems.size());
        invoiceDetail.setInvoiceItems(invoiceItems);


        // 价税合计。单位：元（2位小数）
        BigDecimal total = new BigDecimal("0");
        // 合计税额。单位：元（2位小数）
        BigDecimal totalTax = new BigDecimal("0");
        // 合计金额。不含税，单位：元（2位小数）
        BigDecimal totalAmount = new BigDecimal("0");

        // 开票基础信息
        InvoiceBaseInfo invoiceBaseInfo = InvoiceBaseInfo.builder()
                .classAttr("COMMON_FPKJ_FPT")
                .buyerBankAccount(firstDto.getBuyerBankAccount())
                .buyerInfo(firstDto.getBuyerInfo())
                .buyerName(firstDto.getBuyerName())
                .buyerTaxNum(firstDto.getBuyerTaxNum())
                .encodeVer("编码表版本号")//?
                .invoiceIssuer("开票人")//?
                .originalCode("原发票代码")//?
                .originalNum("原发票号码")//?
                .payee("收款人")//?
                .remark(firstDto.getRemark())
                .reviewer("复核人")//?
                .seiralNum(firstDto.getSeiralNum())
                .sellerBankAccount("销售方银行账号")//?
                .sellerInfo("销售方地址电话")//?
                .sellerName("销售方名称")//?
                .sellerTaxNum("销售方税号")//?
                // 含税标志。0-普通征收 1-减按征收 2-差额征收
                .taxType(0)//?
                // 价税合计。单位：元（2位小数）
                .total(total)//?
                // 合计税额。单位：元（2位小数）
                .totalTax(totalTax)//?
                // 合计金额。不含税，单位：元（2位小数）
                .totalAmount(totalAmount)//?
                // 开票类型：0-蓝字发票；1-红字发票
                .type(0).build();//?

        // 请求信息
        FpkjRequest request = new FpkjRequest();
        request.setClassAttr("REQUEST_COMMON_FPKJ");
        request.setInvoiceBaseInfo(invoiceBaseInfo);
        request.setInvoiceDetail(invoiceDetail);

        // 业务信息
        Business<FpkjRequest> business = new Business();
        business.setId("FPKJ");
        business.setComment("发票开具");
        business.setRequest(request);

        // -------------------------- 3.转xml  ---------------------------------
        String header = "<?xml version=\"1.0\" encoding=\"GBK\"?>\n";
        String xml = beanToXml(business, "GBK", true, true);
        System.out.println(header + xml);
    }

    // 请求报文示例
    //    <?xml version="1.0" encoding="GBK"?>
// <business id="FPKJ" comment="发票开具">
//  <REQUEST_COMMON_FPKJ class="REQUEST_COMMON_FPKJ">
//   <COMMON_FPKJ_FPT class="COMMON_FPKJ_FPT">
//    <FPQQLSH comment="发票请求流水号">2203251420430000001</FPQQLSH>
//    <KPLX comment="开票类型：0-蓝字发票；1-红字发票">0</KPLX>
//    <BMB_BBH comment="编码表版本号">40.0</BMB_BBH>
//    <XSF_NSRSBH	comment="销售方税号">91330108719570880Y</XSF_NSRSBH>
//    <XSF_MC	comment="销售方名称">浙江维尔科技有限公司</XSF_MC>
//    <XSF_DZDH comment="销售方地址电话">浙江省杭州市滨江区立业路528号0571-88158018</XSF_DZDH>
//    <XSF_YHZH comment="销售方银行账号">中国建设银行股份有限公司杭州宝石支行营业部33001616135053001445</XSF_YHZH>
//    <GMF_NSRSBH comment="购买方税号">1</GMF_NSRSBH>
//    <GMF_MC comment="购买方名称">丹阳市货物运输有限公司</GMF_MC>
//    <GMF_DZDH comment="购买方地址电话">1</GMF_DZDH>
//    <GMF_YHZH comment="购买方银行账号">1</GMF_YHZH>
//    <KPR comment="开票人">吴婷婷</KPR>
//    <SKR comment="收款人">李欣</SKR>
//    <FHR comment="复核人">龚建兰</FHR>
//    <YFP_DM comment="原发票代码"></YFP_DM>
//    <YFP_HM comment="原发票号码"></YFP_HM>
//    <JSHJ comment="价税合计">1015.00</JSHJ>
//    <HJJE comment="合计金额">957.55</HJJE>
//    <HJSE comment="合计税额">57.45</HJSE>
//    <HSBZ  comment="含税标志">0</HSBZ>
//    <BZ comment="备注"></BZ>
//   </COMMON_FPKJ_FPT>
//   <COMMON_FPKJ_XMXXS class="COMMON_FPKJ_XMXX" size="1">
//    <COMMON_FPKJ_XMXX>
//     <FPHXZ comment="发票行性质">0</FPHXZ>
//     <SPBM comment="商品编码">3040201990000000000</SPBM>
//     <ZXBM comment="自行编码"></ZXBM>
//     <YHZCBS comment="优惠政策标识"></YHZCBS>
//     <LSLBS comment="零税率标识"></LSLBS>
//     <ZZSTSGL comment="增值税特殊管理"></ZZSTSGL>
//     <XMMC comment="项目名称">运营服务费</XMMC>
//     <GGXH comment="规格型号"></GGXH>
//     <DW comment="单位"></DW>
//     <XMSL comment="项目数量">29</XMSL>
//     <XMDJ comment="项目单价">33.018867924528301887</XMDJ>
//     <XMJE comment="项目金额">957.55</XMJE>
//     <SL comment="税率">0.06</SL>
//     <SE comment="税额">57.45</SE>
//     <KCE comment="扣除额"></KCE>
//    </COMMON_FPKJ_XMXX>
//   </COMMON_FPKJ_XMXXS>
//  </REQUEST_COMMON_FPKJ>
// </business>

    /**
     * 商品清单
     *
     * @author TongDaShuai
     * @since 2022/3/31
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class InventoryImportExcelDto {

        /**
         * 单据号
         */
        @ExcelColumn(name = "单据号")
        private String seiralNum;

        /**
         * 购方名称
         */
        @ExcelColumn(name = "购方名称")
        private String buyerName;

        /**
         * 购方税号
         */
        @ExcelColumn(name = "购方税号")
        private String buyerTaxNum;

        /**
         * 购方地址电话
         */
        @ExcelColumn(name = "购方地址电话")
        private String buyerInfo;

        /**
         * 购方银行账号
         */
        @ExcelColumn(name = "购方银行账号")
        private String buyerBankAccount;

        /**
         * 备注
         */
        @ExcelColumn(name = "备注")
        private String remark;

        /**
         * 序号
         */
        @ExcelColumn(name = "序号")
        private String seiralNo;

        /**
         * 邮箱
         */
        @ExcelColumn(name = "邮箱")
        private String email;

        /**
         * 商品编码简称
         */
        @ExcelColumn(name = "商品编码简称")
        private String shortName;

        /**
         * 商品名称
         */
        @ExcelColumn(name = "商品名称")
        private String name;

        /**
         * 规格型号
         */
        @ExcelColumn(name = "规格型号")
        private String specification;

        /**
         * 计量单位
         */
        @ExcelColumn(name = "计量单位")
        private String unit;

        /**
         * 商品编码
         */
        @ExcelColumn(name = "商品编码")
        private String commodityCode;

        /**
         * 优惠政策标识.0-不使用 1-使用
         */
        @ExcelColumn(name = "优惠政策标识")
        private Integer usePreferential;

        /**
         * 零税率标识。空-非零税率 1-免税 2-不征收 3-普通零税率
         */
        @ExcelColumn(name = "零税率标识")
        private Integer noTaxType;

        /**
         * 优惠政策说明
         */
        @ExcelColumn(name = "优惠政策说明")
        private String preferentialNote;

        /**
         * 数量
         */
        @ExcelColumn(name = "数量")
        private Integer num;

        /**
         * 含税单价
         */
        @ExcelColumn(name = "含税单价")
        private BigDecimal unitPriceWithTax;

        /**
         * 税率。
         */
        @ExcelColumn(name = "税率")
        private BigDecimal taxRate;

        /**
         * 扣除额。
         */
        @ExcelColumn(name = "扣除额")
        private BigDecimal deduction;

        /**
         * 单价
         */
        @ExcelColumn(name = "单价")
        private BigDecimal unitPrice;

        /**
         * 金额.不含税
         */
        @ExcelColumn(name = "金额")
        private BigDecimal amount;

        /**
         * 税额
         */
        @ExcelColumn(name = "税额")
        private BigDecimal tax;

        /**
         * 含税金额
         */
        @ExcelColumn(name = "含税金额")
        private BigDecimal amountWithTax;

        /**
         * 折扣
         */
        @ExcelColumn(name = "折扣")
        private BigDecimal discount;

        /**
         * 含税折扣
         */
        @ExcelColumn(name = "含税折扣")
        private BigDecimal discountWithTax;

        /**
         * 折扣率
         */
        @ExcelColumn(name = "折扣率")
        private BigDecimal discountRate;


    }

    /**
     * 业务信息
     *
     * @author TongDaShuai
     * @since 2022/3/31
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @XmlRootElement(name = "business")
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlSeeAlso({FpkjRequest.class})
    public static class Business<T> {

        /**
         * 业务id
         */
        @XmlAttribute(name = "id")
        private String id;

        /**
         * 业务说明
         */
        @XmlAttribute(name = "comment")
        private String comment;

        /**
         * 请求信息
         * XmLAnyELement这个注解可以去调生成xml中普xsi:type等信息，用这注解姚不能用@XmlElement，需要在泛对应的实体上
         * 增加@XmlRootElement注解.XmlELementwrapper这个注解可以在笼合外层包装一个节点
         */
        @XmlAnyElement
        private T request;
    }

    /**
     * 发票开具请求
     *
     * @author TongDaShuai
     * @since 2022/3/31
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement(name = "REQUEST_COMMON_FPKJ")
    public static class FpkjRequest {

        /**
         * class属性
         */
        @XmlAttribute(name = "class")
        private String classAttr = "REQUEST_COMMON_FPKJ";

        /**
         * 发票的基本信息
         */
        @XmlElement(name = "COMMON_FPKJ_FPT")
        private InvoiceBaseInfo invoiceBaseInfo;

        /**
         * 发票的详细信息
         */
        @XmlElement(name = "COMMON_FPKJ_XMXXS")
        private InvoiceDetail invoiceDetail;
    }






    /**
     * 发票的基本信息
     *
     * @author TongDaShuai
     * @since 2022/3/31
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @XmlAccessorType(XmlAccessType.FIELD)
    @Builder
    public static class InvoiceBaseInfo {

        /**
         * class属性
         */
        @XmlAttribute(name = "class")
        private String classAttr;

        /**
         * 发票请求流水号
         */
        @XmlElement(name = "FPQQLSH")
        private String seiralNum;

        /**
         * 开票类型：0-蓝字发票；1-红字发票
         */
        @XmlElement(name = "KPLX")
        private Integer type;

        /**
         * 编码表版本号
         */
        @XmlElement(name = "BMB_BBH")
        private String encodeVer;

        /**
         * 销售方税号
         */
        @XmlElement(name = "XSF_NSRSBH")
        private String sellerTaxNum;

        /**
         * 销售方名称
         */
        @XmlElement(name = "XSF_MC")
        private String sellerName;

        /**
         * 销售方地址电话
         */
        @XmlElement(name = "XSF_DZDH")
        private String sellerInfo;

        /**
         * 销售方银行账号
         */
        @XmlElement(name = "XSF_YHZH")
        private String sellerBankAccount;

        /**
         * 购买方税号
         */
        @XmlElement(name = "GMF_NSRSBH")
        private String buyerTaxNum;

        /**
         * 购买方名称
         */
        @XmlElement(name = "GMF_MC")
        private String buyerName;

        /**
         * 购买方地址电话
         */
        @XmlElement(name = "XSF_DZDH")
        private String buyerInfo;

        /**
         * 购买方银行账号
         */
        @XmlElement(name = "GMF_YHZH")
        private String buyerBankAccount;

        /**
         * 开票人
         */
        @XmlElement(name = "KPR")
        private String invoiceIssuer;

        /**
         * 收款人
         */
        @XmlElement(name = "SKR")
        private String payee;

        /**
         * 复核人
         */
        @XmlElement(name = "FHR")
        private String reviewer;

        /**
         * 原发票代码
         */
        @XmlElement(name = "YFP_DM")
        private String originalCode;

        /**
         * 原发票号码
         */
        @XmlElement(name = "YFP_HM")
        private String originalNum;

        /**
         * 价税合计。单位：元（2位小数）
         */
        @XmlElement(name = "JSHJ")
        private BigDecimal total;

        /**
         * 合计金额。不含税，单位：元（2位小数）
         */
        @XmlElement(name = "HJJE")
        private BigDecimal totalAmount;

        /**
         * 合计税额。单位：元（2位小数）
         */
        @XmlElement(name = "HJSE")
        private BigDecimal totalTax;

        /**
         * 含税标志。0-普通征收 1-减按征收 2-差额征收
         */
        @XmlElement(name = "HSBZ")
        private Integer taxType;

        /**
         * 备注
         */
        @XmlElement(name = "BZ")
        private String remark;

    }

    /**
     * 发票的明细信息
     *
     * @author TongDaShuai
     * @since 2022/3/31
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class InvoiceDetail {

        /**
         * class属性
         */
        @XmlAttribute(name = "class")
        private String classAttr;

        /**
         * 数量
         */
        @XmlAttribute(name = "size")
        private Integer size;

        /**
         * 发票的子项信息集合
         */
        @XmlElement(name = "COMMON_FPKJ_XMXX")
        private List<InvoiceItem> invoiceItems;
    }

    /**
     * 发票的子项信息
     *
     * @author TongDaShuai
     * @since 2022/3/31
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class InvoiceItem {

        /**
         * 发票行性质。0-正常行 1-折扣行 2-被折扣行
         */
        @XmlElement(name = "FPHXZ")
        private Integer bankType;

        /**
         * 商品编码
         */
        @XmlElement(name = "SPBM")
        private String commodityCode;

        /**
         * 自行编码
         */
        @XmlElement(name = "ZXBM")
        private String selfCode;

        /**
         * 优惠政策标识。0-不使用 1-使用
         */
        @XmlElement(name = "YHZCBS")
        private Integer usePreferential;

        /**
         * 零税率标识。空-非零税率 1-免税 2-不征收 3-普通零税率
         */
        @XmlElement(name = "LSLBS")
        private Integer noTaxType;

        /**
         * 增值税特殊管理
         */
        @XmlElement(name = "ZZSTSGL")
        private String vat;

        /**
         * 项目名称
         */
        @XmlElement(name = "XMMC")
        private String name;

        /**
         * 规格型号
         */
        @XmlElement(name = "GGXH")
        private String specification;

        /**
         * 计量单位
         */
        @XmlElement(name = "DW")
        private String unit;

        /**
         * 项目数量
         */
        @XmlElement(name = "XMSL")
        private Integer num;

        /**
         * 项目单价
         */
        @XmlElement(name = "XMDJ")
        private BigDecimal unitPrice;

        /**
         * 项目金额。不含税，单位：元（2位小数）
         */
        @XmlElement(name = "XMJE")
        private BigDecimal amount;

        /**
         * 税率。小数点后6位 不含税
         */
        @XmlElement(name = "SL")
        private BigDecimal taxRate;

        /**
         * 税额
         */
        @XmlElement(name = "SE")
        private BigDecimal tax;

        /**
         * 扣除额
         */
        @XmlElement(name = "KCE")
        private BigDecimal deduction;

    }

}
